///************************************************************************/
///* Point                                                                */
///************************************************************************/

template <typename T>
template <typename U>
inline GenericPoint2D<T>& GenericPoint2D<T>::operator=(const GenericPoint2D<U>& other)
{
	X = (T)other.X;
	Y = (T)other.Y;
	return *this;
}

template <typename T>
inline GenericPoint2D<T>& GenericPoint2D<T>::operator=(const T& other)
{
	X = other;
	Y = other;
	return *this;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T>& GenericPoint2D<T>::operator+=(const GenericPoint2D<U>& other)
{
	X += (T)other.X;
	Y += (T)other.Y;
	return *this;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T>& GenericPoint2D<T>::operator-=(const GenericPoint2D<U>& other)
{
	X -= (T)other.X;
	Y -= (T)other.Y;
	return *this;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T>& GenericPoint2D<T>::operator*=(const GenericPoint2D<U>& other)
{
	X *= (T)other.X;
	Y *= (T)other.Y;
	return *this;
}

template <typename T>
inline GenericPoint2D<T>& GenericPoint2D<T>::operator*=(const T& other)
{
	X *= other;
	Y *= other;
	return *this;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T> GenericPoint2D<T>::operator+(const GenericPoint2D<U>& other) const
{
	GenericPoint2D<T> pt;
	pt.X = X+(T)other.X;
	pt.Y = Y+(T)other.Y;
	return pt;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T> GenericPoint2D<T>::operator+(const U& other) const
{
	return GenericPoint2D<T>(*this) += (T)other;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T> GenericPoint2D<T>::operator*(const U& other) const
{
	return GenericPoint2D<T>(*this) *= (T)other;
}

template <typename T>
template <typename U>
inline GenericPoint2D<T> GenericPoint2D<T>::operator*(const GenericPoint2D<U>& other) const
{
	return GenericPoint2D<T>(*this) *= (GenericPoint2D<T>)other;
}


template <typename T>
template <typename U>
inline GenericPoint2D<T> GenericPoint2D<T>::operator-(const GenericPoint2D<U>& other) const
{
	GenericPoint2D<T> pt;
	pt.X = X-(T)other.X;
	pt.Y = Y-(T)other.Y;
	return pt;
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::operator-()
{
	GenericPoint2D<T> p;
	p.X = -X;
	p.Y = -Y;
	return p;
}

template <typename T>
inline bool GenericPoint2D<T>::operator==(const GenericPoint2D<T>& other) const
{
	return Math::AbsoluteValue(X-other.X) <= Math::EpsilonValue<T>() &&
		Math::AbsoluteValue(Y-other.Y) <= Math::EpsilonValue<T>();
}

template <typename T>
inline bool GenericPoint2D<T>::operator!=(const GenericPoint2D<T>& other) const
{
	return !(Math::AbsoluteValue(X-other.X) <= Math::EpsilonValue<T>() &&
		Math::AbsoluteValue(Y-other.Y) <= Math::EpsilonValue<T>());
}

template <typename T>
inline bool GenericPoint2D<T>::IsValid() const
{
	return X == X && Y == Y; /// this should be okay for IEEE floats, but what about other archs?
}

template <typename T>
inline T GenericPoint2D<T>::GetDotProduct(const GenericPoint2D<T>& v) const
{
	return X*v.X + Y*v.Y;
}

template <typename T>
inline T GenericPoint2D<T>::GetLength() const
{
	return GTL::Math::SquareRoot(GetLengthSquared());
}

template <typename T>
inline T GenericPoint2D<T>::GetLengthSquared() const
{
	return X*X + Y*Y;
}

template <typename T>
inline T GenericPoint2D<T>::GetDistanceTo(const GenericPoint2D<T>& other) const
{
	return (other-*this).GetLength();
}

template <typename T>
inline T GenericPoint2D<T>::GetDistanceSquaredTo(const GenericPoint2D<T>& other) const
{
	return (other-*this).GetLengthSquared();
}

template <typename T>
inline T GenericPoint2D<T>::NormalizeInPlace()
{
	T len = GetLength();
	if (len > Math::EpsilonValue<T>())
	{
		T inv_len = ((T)1) / len;
		*this *= inv_len;			
		return len;
	}

	return (T)0;
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetNormalized() const
{
	GenericPoint2D<T> pt(*this);
	pt.NormalizeInPlace();
	return pt;
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetLinearInterpolation(const GenericPoint2D<T>& to, T value) const
{
	value = Math::ClampMinMaxValue((T)0, (T)1, value);
	return to + (*this - to) * value;
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetProjectedOnto( const GenericPoint2D<T>& onto ) const
{
	return onto * ( GetDotProduct(onto) / ( onto.GetLengthSquared() ) );
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetRotatedAround(const GenericPoint2D<T>& around, const T& angle_in_radians) const
{
	if (angle_in_radians == (T)0) return *this;
	GenericPoint2D<T> res;

	T sa = Math::Sine(angle_in_radians);
	T ca = Math::Cosine(angle_in_radians);
	res.X = ((X-around.X)*ca - (Y-around.Y)*sa) + around.X;
	res.Y = ((X-around.X)*sa + (Y-around.Y)*ca) + around.Y;

	return res; 
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetRotatedAround(const GenericPoint2D<T>& around, const GenericAngle2D<T>& angle) const
{
	GenericPoint2D<T> res = *this;
	res -= around;
	return res*angle + around;
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetReflectedBy(const GenericPoint2D<T>& mirror) const
{	
	T d = GetDotProduct(mirror.GetNormalized());
	return (*this) - mirror*2*d;
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetAbsolute() const
{
	GenericPoint2D<T> res;
	res.x = AbsoluteValue(X);
	res.y = AbsoluteValue(Y);
	return res;
}

template <typename T>
inline T GenericPoint2D<T>::GetAngle() const
{
	return Just::GetAngle(X, Y);
}

template <typename T>
inline GenericPoint2D<T> GenericPoint2D<T>::GetMovedTowards(const GenericPoint2D<T>& destination, const T& speed, const T& time) const
{
	GenericPoint2D<T> dist = (destination - *this);
	if (dist.GetLengthSquared() < speed*time*speed*time)
		return destination;
	return *this + (destination - *this).GetNormalized() * speed * time;
}

template <typename T>
inline bool GenericPoint2D<T>::IsCircleCollision(const GenericPoint2D<T>& other, const T& radius, const T& other_radius) const
{
	return GetDistanceSquaredTo(other) < (radius+other_radius)*(radius+other_radius);
}

///************************************************************************/
///* Rectangle                                                            */
///************************************************************************/

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::CreateCenteredAtPoint( const GenericPoint2D<T>& at, const T& width, const T& height )
{
	return GenericRectangle2D<T>(at, at).GetEnlargedBy(width/(T)2, height/(T)2);
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::CreateFromPosAndSize(const T& x, const T& y, const T& width, const T& height)
{
	return GenericRectangle2D<T>(x, y, x+width, y+height);
}

template <typename T>
inline GenericRectangle2D<T>& GenericRectangle2D<T>::operator+=(const GenericPoint2D<T>& pt)
{
	P1 += pt;
	P2 += pt;
	return *this;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::operator+(const GenericPoint2D<T>& pt) const
{
	GenericRectangle2D<T> res = *this;
	res += pt;
	return res;
}

template <typename T>
inline GenericRectangle2D<T>& GenericRectangle2D<T>::operator-=(const GenericPoint2D<T>& pt)
{
	P1 -= pt;
	P2 -= pt;
	return *this;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::operator-(const GenericPoint2D<T>& pt) const
{
	GenericRectangle2D<T> res = *this;
	res -= pt;
	return res;
}

template <typename T>
inline GenericRectangle2D<T>& GenericRectangle2D<T>::operator*=(const T& scale)
{
	P1 *= scale;
	P2 *= scale;
	return *this;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::operator*(const T& pt) const
{
	GenericRectangle2D<T> res = *this;
	res *= pt;
	return res;
}

template <typename T>
inline bool GenericRectangle2D<T>::Contains(const GenericPoint2D<T>& point) const
{
	return Contains(point.X, point.Y);
}

template <typename T>
inline bool GenericRectangle2D<T>::Contains(const T& x, const T& y) const
{
	return x>=P1.X&&y>=P1.Y&&x<P2.X&&y<P2.Y;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetIntersection(const GenericRectangle2D<T>& other)
{
	if(IsOverlapping(other))
	{
		return GenericRectangle2D<T>(Math::Maximum(P1.X, other.P1.X), Math::Maximum(P1.Y, other.P1.Y),
			Math::Minimum(P2.X, other.P2.X), Math::Minimum(P2.Y, other.P2.Y));
	}

	return GenericRectangle2D<T>(0,0,0,0);
}

template <typename T>
inline GenericRectangle2D<T>& GenericRectangle2D<T>::IncludePoint(const GenericPoint2D<T>& point)
{
	P1.X = Minimum(P1.X, point.X);
	P1.Y = Minimum(P1.Y, point.Y);
	P2.X = Maximum(P2.X, point.X);
	P2.Y = Maximum(P2.Y, point.Y);
	return *this;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetRectangleMovedInside(const GenericRectangle2D<T>& rect,
																							bool horizontal = true, bool vertical = true)
{
	GenericRectangle2D<T> result = rect;
	if (horizontal)
	{
		T width = rect.P2.X-rect.P1.X;
		T myWidth = P2.X-P1.X;
		if (width > myWidth)
		{
			result.P1.X = P1.X + (myWidth/2 - width/2);
			result.P2.X = P2.X + (myWidth/2 - width/2);
		} else if (rect.P1.X < P1.X)
			result += GenericPoint2D<T>(P1.X-result.P1.X,(T)0);
		else if (rect.P1.X > P2.X-width)
		{
			result.P1.X = P2.X-width;
			result.P2.X = P2.X;
			//result += GenericPoint2D<T>((P2.x-result.P1.x)-width,(T)0);
		}
	}

	if (vertical)
	{
		T height = rect.P2.Y-rect.P1.Y;
		T myHeight = P2.Y-P1.Y;
		if (height > myHeight)
		{
			result.P1.Y = P1.Y + (myHeight/2 - height/2);
			result.P2.Y = P2.Y + (myHeight/2 - height/2);
		} else if (rect.P1.Y < P1.Y)
			result += GenericPoint2D<T>((T)0, P1.Y-result.P1.Y);
		else if (rect.P1.Y > P2.Y - height)
		{
			result.P1.Y = P2.Y-height;
			result.P2.Y = P2.Y;
		}
	}

	return result;
}

template <typename T>
inline bool GenericRectangle2D<T>::IsOverlapping( const GenericRectangle2D<T>& other ) const
{
	return (
		(P1.X <= other.P2.X)&&(P2.X >= other.P1.X)&&
		(P1.Y <= other.P2.Y)&&(P2.Y >= other.P1.Y)
		);
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetTranslated( const T& x, const T& y ) const
{
	return Rectangle(P1+GenericPoint2D<T>(x,y), P2+GenericPoint2D<T>(x,y));
}

template <typename T>
inline GenericRectangle2D<T>& GenericRectangle2D<T>::TranslateInPlace( const T& x, const T& y )
{
	P1 += GenericPoint2D<T>(x,y);
	P2 += GenericPoint2D<T>(x,y);
	return *this;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetMovedTo( const GenericPoint2D<T>& pos ) const
{
	GenericRectangle2D<T> ret(*this);
	ret.P2 += pos-ret.P1;
	ret.P1 = pos;
	return ret;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetEnlargedBy( const T& by ) const { 
	return GenericRectangle2D<T>(P1.X-by, P1.Y-by, P2.X+by, P2.Y+by); 
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetEnlargedBy( const T& byX, const T& byY ) const { 
	return GenericRectangle2D<T>(P1.X-byX, P1.Y-byY, P2.X+byX, P2.Y+byY); 
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetEnlargedBy( const GenericPoint2D<T>& p ) const { 
	return GenericRectangle2D<T>(P1-p, P2+p); 
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetScaledToWidth( const T& width ) const
{
	float sc = float(width)/float(GetWidth());
	T newheight = float(GetHeight())*sc;
	return GetEnlargedBy((width-GetWidth())/2, (newheight-GetHeight())/2);
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetScaledToHeight( const T& height ) const
{
	float sc = float(height)/float(GetHeight());
	T newwidth = float(GetWidth())*sc;
	return GetEnlargedBy((newwidth-GetWidth())/2, (height-GetHeight())/2);
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetResized( const point_type& scale ) const
{
	GenericRectangle2D<T> t = *this;
	t.P2 = t.P1 + t.GetDimensions() * scale;
	return t;
}

template <typename T>
inline GenericRectangle2D<T> GenericRectangle2D<T>::GetTweenedTowards( const GenericRectangle2D<T>& another, float maxspeed ) const
{
	GenericPoint2D<T> P1 = this->P1;
	GenericPoint2D<T> P2 = this->P2;

	GenericPoint2D<T> dir1 = another.P1 - P1;
	GenericPoint2D<T> dir2 = another.P2 - P2;

	T len1 = dir1.GetLength();
	T len2 = dir2.GetLength();

	if (len1 > len2)
	{
		T multiplier = len2/len1;
		if (len1 <= (T)0.001) multiplier = 0;

		if (maxspeed > len1)
			maxspeed = len1;

		P1 += dir1.GetNormalized() * maxspeed;
		P2 += dir2.GetNormalized() * (maxspeed * multiplier);
	} else
	{
		T multiplier = len1/len2;
		if (len2 <= (T)0.001) multiplier = 0;

		if (maxspeed > len2)
			maxspeed = len2;

		P2 += dir2.GetNormalized() * maxspeed;
		P1 += dir1.GetNormalized() * (maxspeed * multiplier);
	}

	return GenericRectangle2D<T>(P1, P2);
}

template <typename T>
inline GenericRectangle2D<T>& GenericRectangle2D<T>::Correct()
{
	if (P1.X > P2.X) { T temp = P1.X; P1.X = P2.X; P2.X = temp; }
	if (P1.Y > P2.Y) { T temp = P1.Y; P1.Y = P2.Y; P2.Y = temp; }
	return *this;
}

/// assumes correctness
template <typename T>
inline GenericPoint2D<T> GenericRectangle2D<T>::GetDimensions() const
{
	return P2-P1;
}

/// assumes correctness
template <typename T>
inline GenericPoint2D<T> GenericRectangle2D<T>::GetCenter() const
{
	return GenericPoint2D<T>(P1.X+(P2.X-P1.X)/((T)2), P1.Y+(P2.Y-P1.Y)/((T)2));
}
